package de.jonasjelten.jcalc.api;

import de.jonasjelten.jcalc.exception.JCalcException;
import de.jonasjelten.jcalc.exception.JParserException;
import de.jonasjelten.jcalc.math.*;
import de.jonasjelten.jcalc.mathExps.JMathExp;
import de.jonasjelten.jcalc.math.JThreadGroup;
import de.jonasjelten.jcalc.parser.JParser;
import java.math.BigInteger;

/**
 * Dies ist die Hauptklasse von jCalc
 * Sie ist das API zu jCalc, andere Anwendungen greifen nur auf diese Klasse zu.
 * @author Jonas Jelten
 */
public final class JCalc {

    public JCalcInterface client;
    public boolean finished = false;
    public int totalNonTrivialMathExpsCount;
    private int finishedNonTrivialMathExpsCount;
    int resultcount;
    public JThreadGroup expgroup;
    JVarTable cache;
    JInProgressTable prg;
    JParser parser;
    String input;

    public JMathExp parse(String input) throws JCalcException {
        this.input = input.toLowerCase().trim().replaceAll("\\s", "");
        cache = new JVarTable();
        prg = new JInProgressTable();
        resultcount = 0;

        if (expgroup != null) {
            expgroup.destroy();
        }
        this.expgroup = new JThreadGroup("Calculation-Threads", cache, null, prg);

        return parser.parse(this.input);
    }

    public JCalc(JCalcInterface jcalcclient) {
        this.client = jcalcclient;
        try {
            parser = new JParser(this);
        } catch (JParserException ex) {
            errorMessage(ex.getMessage());
        }
    }

    public void echoProgress(String txt) {
        client.echo(txt);
    }

    public void errorMessage(String txt) {
        client.errorMessage(txt);
    }

    public void updateProgressOnMathExpFinished(JMathExp mathExpThatJustFinished, boolean isCached) { // maybe not synchronized because of new-thread-starting


        int abwievielkuerzen = JPreferences.abwievielkürzen;
        int aufwievielekuerzen = JPreferences.aufwievielekürzen;

        finishedNonTrivialMathExpsCount++;
        int progress = (100 * finishedNonTrivialMathExpsCount) / totalNonTrivialMathExpsCount;


        String mathExp = mathExpThatJustFinished.forceResult().toString();

        BigInteger strlen = BigInteger.valueOf((long) mathExp.length());
        String what;
        String howmuchleft = "";
        String evalue = "";
        // lets remove the minus
        if (strlen.compareTo(BigInteger.ZERO) < 0) {
            strlen.subtract(BigInteger.ONE);
        }
        if (strlen.compareTo(BigInteger.valueOf(abwievielkuerzen)) > 0) {
            what = mathExp.substring(0, aufwievielekuerzen);
            howmuchleft = ".... " + strlen.subtract(BigInteger.valueOf(aufwievielekuerzen)) + " more digits";
        } else {
            what = mathExp;
        }
        String iscache = isCached ? "Cached" : "Intermediate";
        synchronized (this) {
            if (progress == 100) {
                progress = 99;
            }
            //TODO: Dies hier ist dir funktionierende aktivthreadanzahl!
            //echoProgress("Threadzahl: " + calcthreads.activeCount());

            client.intermediateResult(++resultcount + ". " + iscache + " result of " + mathExpThatJustFinished.toString() + " (" + strlen + " digits): "
                    + " = " + what + howmuchleft + evalue
                    + "\r\n Calculation progress: " + progress + "% Threads: active:" + (expgroup.runningThreads) + " running: " + (expgroup.activeCount() - 1));
            client.setProgress(progress);
        }
    }

    public void evalMathExp(final JMathExp root) {
        totalNonTrivialMathExpsCount = root.getNonTrivialMathExpsCount();
        finishedNonTrivialMathExpsCount = 0;

        final long startTime = System.currentTimeMillis();

        echoProgress("Entered expression: " + input + "\r\n"
                + "Mathematical expression to be evaluated: " + root
                + "\r\nTotal non-trivial math expressios: "
                + totalNonTrivialMathExpsCount + "\r\n Initializing evaluation...");


        Runnable evr = new Runnable() {

            @Override
            public void run() {

                root.start();


                int abwievielkuerzen = JPreferences.abwievielkürzen;
                int aufwievielekuerzen = JPreferences.aufwievielekürzen;

                // evaluate all
                String evaluationResult = root.getEvalResult().toString();
                // done
                long evaluationDurationMs = System.currentTimeMillis() - startTime;

                BigInteger strlen = BigInteger.valueOf(evaluationResult.length());

                String what;
                String howmuchleft = "";
                // lets remove the minus
                if (strlen.compareTo(BigInteger.ZERO) < 0) {
                    strlen.subtract(BigInteger.ONE);
                }
                if (strlen.compareTo(BigInteger.valueOf(abwievielkuerzen)) > 0) {
                    what = evaluationResult.substring(0, aufwievielekuerzen);
                    howmuchleft = ".... " + strlen.subtract(BigInteger.valueOf(aufwievielekuerzen)) + " more digits";
                } else {
                    what = evaluationResult;
                }
                client.finishedCalculating(
                        "Evaluation completed.\r\nResult (" + strlen + " digits): "
                        + what + howmuchleft
                        + "\r\nSeconds used: "
                        + (evaluationDurationMs / ((double) 1000)));

                /*int erglength = evaluationResult.length();
                if (evaluationResult.charAt(0) == '-') {
                erglength--;
                }
                digitcount.setText(erglength + "");

                 */
                client.finalResult(evaluationResult);
                client.setProgress(100);
                resultcount = 0;
            }
        };
        Thread evaluator = new Thread(evr, "Evaluation-Thread");
        // Set the thread priority for this thread and all subthreads.
        //evaluator.setPriority(1); // from 1-10, 1 is lowest.
        evaluator.setPriority(Thread.MIN_PRIORITY);
        evaluator.start();
    }
}
